﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using Property.DelegateBased;
using Property;
namespace Property.Samples.Concepts
{
    public interface IObjectWithIdentity
    {
        Guid Identity
        {
            get; set;
        }
    }

    public class Concept:INotifyPropertyChanged,IObjectWithIdentity
    {
        //hiding constructor
        protected Concept(){}

        public virtual bool Modified { get; set; }

        #region Implementation of IObjectWithIdentity

        public virtual Guid Identity { get; set; }

        #endregion

        #region Implementation of INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected void onPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;

            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        //implementation declared as nested type to gain access to private members of Concept.
        public class Implementation<TConcept> : DefaultImplementation<TConcept> where TConcept: Concept
        {
            public override Func<TBaseType, TResult> ImplementGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                throw new ArgumentException("Can't implement new property. Just override.");
            }
            public override Action<TBaseType, TResult> ImplementSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                throw new ArgumentException("Can't implement new property. Just override.");
            }
            public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                //this code will run once per implementing property
                return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
            }
            public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                //this code will run once per implementing property
                var propertyName = property.Name;
                var setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(propertyName);
                var getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(propertyName);
                var comparer = EqualityComparer<TResult>.Default;

                return (pthis, value) =>
                {//constructed delegate will run every time accessing property.
                    if (!comparer.Equals(value, getter(pthis)))
                    {
                        setter(pthis, value);
                        pthis.onPropertyChanged(new PropertyChangedEventArgs(propertyName));
                    }
                };
            }
        }
    }
}
